// 任意精度计算的计算器
// 使用 Polish suffix notation
/*
 * ~ + - * / % ^ 取反 加 减 乘 除 取模 取幂
 * d p f q 复制栈顶的值 输出栈顶的值 自顶向下输出栈中的值 退出
**/
#include "ap.h"
#include "stack.h"
#include "fmt.h"

#include <stdio.h>
#include <ctype.h>

static void
usage()
{
    Fmt_fprint(stdout,
            "    input: 0123456789 "
            "    arithmetic: ~ + - * / % ^\n"
            "    oper: d(uplicate) p(rint) f(ull) q(uit)\n"
    );
}

static AP_T
pop(Stack_T sp)
{
    if (!Stack_empty(sp)) {
        return Stack_pop(sp);
    } else {
        Fmt_fprint(stderr, "?stack underflow\n");
        return AP_new(0);
    }
}

static void
clearstack(Stack_T sp)
{
    while (!Stack_empty(sp)) {
        AP_T x = Stack_pop(sp);
        AP_free(&x);
    }
}

static void
casenum(Stack_T sp, int c)
{
    char buf[512];
    int i = 0;
    int len = (int)sizeof(buf) - 1;
    for (i=0; c != EOF && isdigit(c); c = getchar(), i++) {
        if (i < len)
            buf[i] = c;
    }
    if (i > len-1) {
        i = len-1;
        Fmt_fprint(stderr, "?integer constant exceeds %d digits\n", i);
    }
    buf[i] = 0;
    if (c != EOF)
        ungetc(c, stdin);

    Stack_push(sp, AP_fromstr(buf, 10, NULL));
}

static void
casef(Stack_T sp)
{
    if (Stack_empty(sp))
        return;
    Stack_T tmp = Stack_new();
    while (!Stack_empty(sp)) {
        AP_T x = pop(sp);
        Fmt_print("%D ", x);
        Stack_push(tmp, x);
    }
    Fmt_fprint(stdout, "\n");

    while (!Stack_empty(tmp))
        Stack_push(sp, Stack_pop(tmp));
    Stack_free(&tmp);
}

static int
dealcase(Stack_T sp, int c)
{
    switch (c) {
        case ' ':
        case '\t':
        case '\n':
        case '\f':
        case '\r':
            break;

        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            casenum(sp, c);
            break;

        case '+': {
            AP_T y = pop(sp), x = pop(sp);
            Stack_push(sp, AP_add(x, y));
            AP_free(&x); AP_free(&y);
            break;
        }

        case '-': {
            AP_T y = pop(sp), x = pop(sp);
            Stack_push(sp, AP_sub(x, y));
            AP_free(&x); AP_free(&y);
            break;
        }

        case '*': {
            AP_T y = pop(sp), x = pop(sp);
            Stack_push(sp, AP_mul(x, y));
            AP_free(&x); AP_free(&y);
            break;
        }

        case '/': {
            AP_T y = pop(sp), x = pop(sp);
            if (AP_cmpi(y, 0) == 0) {
                Fmt_fprint(stderr, "?/ by 0\n");
                Stack_push(sp, AP_new(0));
            } else {
                Stack_push(sp, AP_div(x, y));
            }
            AP_free(&x); AP_free(&y);
            break;
        }

        case '%': {
            AP_T y = pop(sp), x = pop(sp);
            if (AP_cmpi(y, 0) == 0) {
                Fmt_fprint(stderr, "?%% by 0\n");
                Stack_push(sp, AP_new(0));
            } else {
                Stack_push(sp, AP_mod(x, y));
            }
            AP_free(&x); AP_free(&y);
            break;
        }

        case '^': {
            AP_T y = pop(sp), x = pop(sp);
            if (AP_cmpi(y, 0) <= 0) {
                Fmt_fprint(stderr, "?nonpositive power\n");
                Stack_push(sp, AP_new(0));
            } else {
                Stack_push(sp, AP_pow(x, y, NULL));
            }
            AP_free(&x); AP_free(&y);
            break;
        }

        case '~': {
            AP_T x = pop(sp);
            Stack_push(sp, AP_neg(x));
            AP_free(&x);
            break;
        }
        

        case 'd': {
            AP_T x = pop(sp);
            Stack_push(sp, x);
            Stack_push(sp, AP_addi(x, 0));
            break;
        }

        case 'p': {
            AP_T x = pop(sp);
            Fmt_print("%D\n", x);
            Stack_push(sp, x);
            break;
        }
        
        case 'f':
            casef(sp);
            break;

        case 'c':
            clearstack(sp);
            break;
        case 'h':
            usage();
            break;

        default:
            return 1;
    }
    return 0;
}

static void
loop(Stack_T sp)
{
    int c;
    usage();
    while ((c = getchar()) != EOF) { 
        if (c == 'q')
            break;

        if (dealcase(sp, c)) {
            if (isprint(c))
                Fmt_fprint(stderr, "?'%c'", c);
            else
                Fmt_fprint(stderr, "?\\%03o", c);
            Fmt_fprint(stderr, " is unimplemented\n");
        }

        fflush(stdout);
        fflush(stderr);
    }
}

int main()
{
    Stack_T sp = Stack_new();
    Fmt_register('D', AP_fmt);

    loop(sp);

    clearstack(sp);
    Stack_free(&sp);
    return 0;
}

